using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing.Design;
using System.Xml.Serialization;
using Microsoft.VisualBasic;

namespace Creek.UI.PropertyGridEx
{
    [Serializable, XmlRoot("CustomProperty")]
    public class CustomProperty
    {
        #region "Private variables"

        // Common properties
        protected UICustomEventEditor.OnClick MethodDelegate;
        protected bool bIsBrowsable = false;
        protected bool bIsDropdownResizable = false;
        protected bool bIsPassword = false;
        protected bool bIsPercentage = false;
        protected bool bIsReadOnly = false;
        protected bool bParenthesize = false;
        protected bool bRef = false;

        // Filename editor properties
        protected bool bUseFileNameEditor = false;
        protected bool bVisible = true;

        // Custom choices properties

        protected BrowsableTypeConverter.LabelStyle eBrowsablePropertyLabel =
            BrowsableTypeConverter.LabelStyle.lsEllipsis;

        protected UIFilenameEditor.FileDialogType eDialogType = UIFilenameEditor.FileDialogType.LoadFileDialog;
        protected CustomChoices oChoices = null;

        // Dynamic properties

        // Extended Attributes
        [NonSerialized] protected AttributeCollection oCustomAttributes = null;

        // Custom Editor and Custom Type Converter
        [NonSerialized] protected UITypeEditor oCustomEditor = null;
        [NonSerialized] protected TypeConverter oCustomTypeConverter = null;
        protected object oDatasource = null;
        protected Type oDefaultType = null;
        protected object oDefaultValue = null;
        protected object oRef = null;
        protected object oSelectedItem = null;
        protected object oSelectedValue = null;
        protected object oTag = null;
        protected object oValue = null;
        protected string sCategory = "";
        protected string sDescription = "";
        protected string sDisplayMember = null;
        protected string sFilter = null;
        protected string sName = "";
        protected string sProp = "";
        protected string sValueMember = null;

        #endregion

        #region "Public methods"

        public CustomProperty()
        {
            sName = "New Property";
            oValue = new string(' ', 0);
        }

        public CustomProperty(string strName, object objValue, bool boolIsReadOnly, string strCategory,
                              string strDescription, bool boolVisible)
        {
            sName = strName;
            oValue = objValue;
            bIsReadOnly = boolIsReadOnly;
            sDescription = strDescription;
            sCategory = strCategory;
            bVisible = boolVisible;
            if (oValue != null)
            {
                oDefaultValue = oValue;
            }
        }

        public CustomProperty(string strName, ref object objRef, string strProp, bool boolIsReadOnly, string strCategory,
                              string strDescription, bool boolVisible)
        {
            sName = strName;
            bIsReadOnly = boolIsReadOnly;
            sDescription = strDescription;
            sCategory = strCategory;
            bVisible = boolVisible;
            bRef = true;
            oRef = objRef;
            sProp = strProp;
            if (Value != null)
            {
                oDefaultValue = Value;
            }
        }

        public void RebuildAttributes()
        {
            if (bUseFileNameEditor)
            {
                BuildAttributes_FilenameEditor();
            }
            else if (oChoices != null)
            {
                BuildAttributes_CustomChoices();
            }
            else if (oDatasource != null)
            {
                BuildAttributes_ListboxEditor();
            }
            else if (bIsBrowsable)
            {
                BuildAttributes_BrowsableProperty();
            }
        }

        #endregion

        #region "Private methods"

        private object DataColumn
        {
            get
            {
                var oRow = (DataRow) oRef;
                if (oRow.RowState != DataRowState.Deleted)
                {
                    if (oDatasource == null)
                    {
                        return oRow[sProp];
                    }
                    else
                    {
                        var oLookupTable = oDatasource as DataTable;
                        if (oLookupTable != null)
                        {
                            return oLookupTable.Select(sValueMember + "=" + oRow[sProp])[0][sDisplayMember];
                        }
                        else
                        {
                            Information.Err().Raise(Constants.vbObjectError + 513, null,
                                                    "Bind of DataRow with a DataSource that is not a DataTable is not possible",
                                                    null, null);
                            return null;
                        }
                    }
                }
                else
                {
                    return null;
                }
            }
            set
            {
                var oRow = (DataRow) oRef;
                if (oRow.RowState != DataRowState.Deleted)
                {
                    if (oDatasource == null)
                    {
                        oRow[sProp] = value;
                    }
                    else
                    {
                        var oLookupTable = oDatasource as DataTable;
                        if (oLookupTable != null)
                        {
                            if (oLookupTable.Columns[sDisplayMember].DataType.Equals(Type.GetType("System.String")))
                            {
                                oRow[sProp] =
                                    oLookupTable.Select(oLookupTable.Columns[sDisplayMember].ColumnName + " = \'" +
                                                        value + "\'")[0][sValueMember];
                            }
                            else
                            {
                                oRow[sProp] =
                                    oLookupTable.Select(oLookupTable.Columns[sDisplayMember].ColumnName + " = " + value)
                                        [0][sValueMember];
                            }
                        }
                        else
                        {
                            Information.Err().Raise(Constants.vbObjectError + 514, null,
                                                    "Bind of DataRow with a DataSource that is not a DataTable is impossible",
                                                    null, null);
                        }
                    }
                }
            }
        }

        private void BuildAttributes_FilenameEditor()
        {
            var attrs = new ArrayList();
            var FilterAttribute = new UIFilenameEditor.FileDialogFilterAttribute(sFilter);
            var SaveDialogAttribute = new UIFilenameEditor.SaveFileAttribute();
            Attribute[] attrArray;
            attrs.Add(FilterAttribute);
            if (eDialogType == UIFilenameEditor.FileDialogType.SaveFileDialog)
            {
                attrs.Add(SaveDialogAttribute);
            }
            attrArray = (Attribute[]) attrs.ToArray(typeof (Attribute));
            oCustomAttributes = new AttributeCollection(attrArray);
        }

        private void BuildAttributes_CustomChoices()
        {
            if (oChoices != null)
            {
                var list = new CustomChoices.CustomChoicesAttributeList(oChoices.Items);
                var attrs = new ArrayList();
                Attribute[] attrArray;
                attrs.Add(list);
                attrArray = (Attribute[]) attrs.ToArray(typeof (Attribute));
                oCustomAttributes = new AttributeCollection(attrArray);
            }
        }

        private void BuildAttributes_ListboxEditor()
        {
            if (oDatasource != null)
            {
                var ds = new UIListboxEditor.UIListboxDatasource(ref oDatasource);
                var vm = new UIListboxEditor.UIListboxValueMember(sValueMember);
                var dm = new UIListboxEditor.UIListboxDisplayMember(sDisplayMember);
                UIListboxEditor.UIListboxIsDropDownResizable ddr = null;
                var attrs = new ArrayList();
                attrs.Add(ds);
                attrs.Add(vm);
                attrs.Add(dm);
                if (bIsDropdownResizable)
                {
                    ddr = new UIListboxEditor.UIListboxIsDropDownResizable();
                    attrs.Add(ddr);
                }
                Attribute[] attrArray;
                attrArray = (Attribute[]) attrs.ToArray(typeof (Attribute));
                oCustomAttributes = new AttributeCollection(attrArray);
            }
        }

        private void BuildAttributes_BrowsableProperty()
        {
            var style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(eBrowsablePropertyLabel);
            oCustomAttributes = new AttributeCollection(new Attribute[] {style});
        }

        private void BuildAttributes_CustomEventProperty()
        {
            var attr = new UICustomEventEditor.DelegateAttribute(MethodDelegate);
            oCustomAttributes = new AttributeCollection(new Attribute[] {attr});
        }

        #endregion

        #region "Public properties"

        [Category("Appearance"), DisplayName("Name"), Description("Display Name of the CustomProperty."),
         ParenthesizePropertyName(true), XmlElement("Name")]
        public string Name
        {
            get { return sName; }
            set { sName = value; }
        }

        [Category("Appearance"), DisplayName("ReadOnly"), Description("Set read only attribute of the CustomProperty."),
         XmlElement("ReadOnly")]
        public bool IsReadOnly
        {
            get { return bIsReadOnly; }
            set { bIsReadOnly = value; }
        }

        [Category("Appearance"), Description("Set visibility attribute of the CustomProperty.")]
        public bool Visible
        {
            get { return bVisible; }
            set { bVisible = value; }
        }

        [Category("Appearance"), Description("Represent the Value of the CustomProperty.")]
        public object Value
        {
            get
            {
                if (bRef)
                {
                    if (oRef.GetType() == typeof (DataRow) || oRef.GetType().IsSubclassOf(typeof (DataRow)))
                        return DataColumn;
                    else
                        return Interaction.CallByName(oRef, sProp, CallType.Get, null);
                }
                else
                {
                    return oValue;
                }
            }
            set
            {
                if (bRef)
                {
                    if (oRef.GetType() == typeof (DataRow) || oRef.GetType().IsSubclassOf(typeof (DataRow)))
                        DataColumn = value;
                    else
                        Interaction.CallByName(oRef, sProp, CallType.Set, value);
                }
                else
                {
                    oValue = value;
                }
            }
        }

        [Category("Appearance"), Description("Set description associated with the CustomProperty.")]
        public string Description
        {
            get { return sDescription; }
            set { sDescription = value; }
        }

        [Category("Appearance"), Description("Set category associated with the CustomProperty.")]
        public string Category
        {
            get { return sCategory; }
            set { sCategory = value; }
        }

        [XmlIgnore]
        public Type Type
        {
            get
            {
                if (Value != null)
                {
                    return Value.GetType();
                }
                else
                {
                    if (oDefaultValue != null)
                    {
                        return oDefaultValue.GetType();
                    }
                    else
                    {
                        return oDefaultType;
                    }
                }
            }
        }

        [XmlIgnore]
        public AttributeCollection Attributes
        {
            get { return oCustomAttributes; }
            set { oCustomAttributes = value; }
        }

        [Category("Behavior"), Description("Indicates if the property is browsable or not."),
         XmlElement(IsNullable = false)]
        public bool IsBrowsable
        {
            get { return bIsBrowsable; }
            set
            {
                bIsBrowsable = value;
                if (value)
                {
                    BuildAttributes_BrowsableProperty();
                }
            }
        }

        [Category("Appearance"), DisplayName("Parenthesize"),
         Description(
             "Indicates whether the name of the associated property is displayed with parentheses in the Properties window."
             ), DefaultValue(false), XmlElement("Parenthesize")]
        public bool Parenthesize
        {
            get { return bParenthesize; }
            set { bParenthesize = value; }
        }

        [Category("Behavior"), Description("Indicates the style of the label when a property is browsable."),
         XmlElement(IsNullable = false)]
        public BrowsableTypeConverter.LabelStyle BrowsableLabelStyle
        {
            get { return eBrowsablePropertyLabel; }
            set
            {
                bool Update = false;
                if (value != eBrowsablePropertyLabel)
                {
                    Update = true;
                }
                eBrowsablePropertyLabel = value;
                if (Update)
                {
                    var style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(value);
                    oCustomAttributes = new AttributeCollection(new Attribute[] {style});
                }
            }
        }

        [Category("Behavior"), Description("Indicates if the property is masked or not."),
         XmlElement(IsNullable = false)]
        public bool IsPassword
        {
            get { return bIsPassword; }
            set { bIsPassword = value; }
        }

        [Category("Behavior"), Description("Indicates if the property represents a value in percentage."),
         XmlElement(IsNullable = false)]
        public bool IsPercentage
        {
            get { return bIsPercentage; }
            set { bIsPercentage = value; }
        }

        [Category("Behavior"), Description("Indicates if the property uses a FileNameEditor converter."),
         XmlElement(IsNullable = false)]
        public bool UseFileNameEditor
        {
            get { return bUseFileNameEditor; }
            set { bUseFileNameEditor = value; }
        }

        [Category("Behavior"), Description("Apply a filter to FileNameEditor converter."),
         XmlElement(IsNullable = false)]
        public string FileNameFilter
        {
            get { return sFilter; }
            set
            {
                bool UpdateAttributes = false;
                if (value != sFilter)
                {
                    UpdateAttributes = true;
                }
                sFilter = value;
                if (UpdateAttributes)
                {
                    BuildAttributes_FilenameEditor();
                }
            }
        }

        [Category("Behavior"), Description("DialogType of the FileNameEditor."), XmlElement(IsNullable = false)]
        public UIFilenameEditor.FileDialogType FileNameDialogType
        {
            get { return eDialogType; }
            set
            {
                bool UpdateAttributes = false;
                if (value != eDialogType)
                {
                    UpdateAttributes = true;
                }
                eDialogType = value;
                if (UpdateAttributes)
                {
                    BuildAttributes_FilenameEditor();
                }
            }
        }

        [Category("Behavior"), Description("Custom Choices list."), XmlIgnore]
        public CustomChoices Choices
        {
            get { return oChoices; }
            set
            {
                oChoices = value;
                BuildAttributes_CustomChoices();
            }
        }

        [Category("Databinding"), XmlIgnore]
        public object Datasource
        {
            get { return oDatasource; }
            set
            {
                oDatasource = value;
                BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding"), XmlElement(IsNullable = false)]
        public string ValueMember
        {
            get { return sValueMember; }
            set
            {
                sValueMember = value;
                BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding"), XmlElement(IsNullable = false)]
        public string DisplayMember
        {
            get { return sDisplayMember; }
            set
            {
                sDisplayMember = value;
                BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding"), XmlElement(IsNullable = false)]
        public object SelectedValue
        {
            get { return oSelectedValue; }
            set { oSelectedValue = value; }
        }

        [Category("Databinding"), XmlElement(IsNullable = false)]
        public object SelectedItem
        {
            get { return oSelectedItem; }
            set { oSelectedItem = value; }
        }

        [Category("Databinding"), XmlElement(IsNullable = false)]
        public bool IsDropdownResizable
        {
            get { return bIsDropdownResizable; }
            set
            {
                bIsDropdownResizable = value;
                BuildAttributes_ListboxEditor();
            }
        }

        [XmlIgnore]
        public UITypeEditor CustomEditor
        {
            get { return oCustomEditor; }
            set { oCustomEditor = value; }
        }

        [XmlIgnore]
        public TypeConverter CustomTypeConverter
        {
            get { return oCustomTypeConverter; }
            set { oCustomTypeConverter = value; }
        }

        [XmlIgnore]
        public object Tag
        {
            get { return oTag; }
            set { oTag = value; }
        }

        [XmlIgnore]
        public object DefaultValue
        {
            get { return oDefaultValue; }
            set { oDefaultValue = value; }
        }

        [XmlIgnore]
        public Type DefaultType
        {
            get { return oDefaultType; }
            set { oDefaultType = value; }
        }

        [XmlIgnore]
        public UICustomEventEditor.OnClick OnClick
        {
            get { return MethodDelegate; }
            set
            {
                MethodDelegate = value;
                BuildAttributes_CustomEventProperty();
            }
        }

        #endregion

        #region "CustomPropertyDescriptor"

        public class CustomPropertyDescriptor : PropertyDescriptor
        {
            protected CustomProperty oCustomProperty;

            public CustomPropertyDescriptor(CustomProperty myProperty, Attribute[] attrs) : base(myProperty.Name, attrs)
            {
                if (myProperty == null)
                {
                    oCustomProperty = null;
                }
                else
                {
                    oCustomProperty = myProperty;
                }
            }

            public override Type ComponentType
            {
                get { return GetType(); }
            }

            public override bool IsReadOnly
            {
                get { return oCustomProperty.IsReadOnly; }
            }

            public override Type PropertyType
            {
                get { return oCustomProperty.Type; }
            }

            public override string Description
            {
                get { return oCustomProperty.Description; }
            }

            public override string Category
            {
                get { return oCustomProperty.Category; }
            }

            public override string DisplayName
            {
                get { return oCustomProperty.Name; }
            }

            public override bool IsBrowsable
            {
                get { return oCustomProperty.IsBrowsable; }
            }

            public object CustomProperty
            {
                get { return oCustomProperty; }
            }

            public override bool CanResetValue(object component)
            {
                if ((oCustomProperty.DefaultValue != null) || (oCustomProperty.DefaultType != null))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public override object GetValue(object component)
            {
                return oCustomProperty.Value;
            }

            public override void ResetValue(object component)
            {
                oCustomProperty.Value = oCustomProperty.DefaultValue;
                OnValueChanged(component, EventArgs.Empty);
            }

            public override void SetValue(object component, object value)
            {
                oCustomProperty.Value = value;
                OnValueChanged(component, EventArgs.Empty);
            }

            public override bool ShouldSerializeValue(object component)
            {
                object oValue = oCustomProperty.Value;
                if ((oCustomProperty.DefaultValue != null) && (oValue != null))
                {
                    return ! oValue.Equals(oCustomProperty.DefaultValue);
                }
                else
                {
                    return false;
                }
            }
        }

        #endregion
    }
}